package com.cinyi.ormlite.web.dao;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.cinyi.ormlite.core.BaseModel;
import com.cinyi.ormlite.core.TableConfig;
import com.cinyi.ormlite.core.TableConfigCache;
import com.cinyi.ormlite.dao.DatabaseAdapter;
import com.cinyi.ormlite.dao.SQLModel;
import com.cinyi.ormlite.web.util.DbUtil;

/**
 * jdbc操作
 * @author penghui
 *
 */
@SuppressWarnings("rawtypes")
public class JdbcDatabaseAdapter<T extends BaseModel<?>> extends DatabaseAdapter<T>{
    /**
     * LOG
     */
	private static final Logger logger = Logger.getLogger(JdbcDatabaseAdapter.class);
	
	private JdbcGenericDaoImpl jdbc ;
	
	public JdbcDatabaseAdapter(JdbcGenericDaoImpl jdbc){
		this.jdbc = jdbc ;
	}
	
	public Integer executeSQL(String sql,List<Object> params) throws Exception {
		PreparedStatement ps = getConnection().prepareStatement(sql);
		if(params != null && params.size() > 0){
			fillParameters(params, ps);
		}
		
		//执行SQL
		int result = ps.executeUpdate();
		DbUtil.release(ps,null);
		
		return result  ;
	}
	

    public ResultSet executeQuery(String sql,List<Object> params) throws Exception {
        Connection conn = getConnection() ;
        PreparedStatement ps = null ;
        
        ps = conn.prepareStatement(sql);
        if (params != null && params.size() > 0) {
            fillParameters(params, ps);
        }
        
		logger.debug("SQL:"+sql);

        return ps.executeQuery();
        
    }

	/**
	 * 设置SQL参数占位符的值
	 */
	private void fillParameters(List<Object> values, PreparedStatement ps)
			throws SQLException {
		if(values==null || values.size() == 0 )return ;
		for (int i = 1; i <= values.size(); i++) {
			Object fieldValue = values.get(i-1);
			if(fieldValue == null){
				continue ;
			}
			
			Class<?> clazzValue = fieldValue.getClass();
			if (clazzValue == String.class) {
				ps.setString(i,(String)fieldValue);
			} else if (clazzValue == boolean.class || clazzValue == Boolean.class) {
				ps.setBoolean(i, (Boolean)fieldValue);
			} else if (clazzValue == byte.class || clazzValue == Byte.class) {
				ps.setByte(i, (Byte)fieldValue);
			} else if (clazzValue == char.class || clazzValue == Character.class) {
				ps.setObject(i, fieldValue,Types.CHAR);
			} else if (clazzValue == Date.class|| clazzValue == Timestamp.class) {
				ps.setTimestamp(i, new Timestamp(((Date) fieldValue).getTime()));
			} else if (clazzValue.isArray()) {
				Object[] arrayValue = (Object[]) fieldValue;
				StringBuffer sb = new StringBuffer();
				for (int j = 0; j < arrayValue.length; j++) {
					sb.append(arrayValue[j]).append("、");
				}
				ps.setString(i, sb.deleteCharAt(sb.length()-1).toString());
			} else {
				ps.setObject(i, fieldValue, Types.NUMERIC);
			}
		}
	}
	
	public Connection getConnection() {
		return jdbc.getConnection();
	}

	@SuppressWarnings("unchecked")
	@Override
	public Integer executeSQL(SQLModel model) throws Exception {
		return executeSQL(model.getSql(), model.getData());
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> executeQuery(SQLModel<T> model) throws Exception {
		List<T> list = new ArrayList<T>();
		TableConfig cfg = TableConfigCache.getConfig(model.getClazz());
		Field[] fields = cfg.getFields();
		
        List<Object> params = model.getData();
        
        ResultSet rs = executeQuery(model.getSql(),params);
		Map<String,Integer> columnLables = createResultMeta(rs);
		while(rs.next()) {
			T t = (T) model.getClazz().newInstance();
			createObject(t, columnLables , fields, rs);
			list.add(t);
		}
		//释放资源
		DbUtil.release(null, rs);
		
		return list;
		
	}

	
	@Override
	public Long count(SQLModel model) throws Exception {
		return null;
	}

	private Map<String,Integer> createResultMeta(ResultSet rs) throws SQLException{
		Map<String,Integer> keys = new  HashMap<String,Integer>();
		ResultSetMetaData rsmd = rs.getMetaData();
		int count=rsmd.getColumnCount();
		for(int i=0;i<count;i++){
			String col = rsmd.getColumnLabel(i+1) ;
			keys.put(col,i);
		}
		
		return keys ;
	}
	
	/**
	 * 根据结果集初始化对象
	 */
	private void createObject(T t,Map<String,Integer> columnLabels , Field[] fields, ResultSet rs)
			throws SQLException, IntrospectionException,
			IllegalAccessException, InvocationTargetException {
		for (Field field : fields) {
			String propertyName = field.getName();
			Object paramVal = null;
			Class<?> clazzField = field.getType();
			
			if( !columnLabels.containsKey(propertyName)){
//				logg.debug("column not found  cfdsg:" + propertyName );
				continue ;
			}
			
			if (clazzField == String.class) {
				paramVal = rs.getString(propertyName);
			} else if (clazzField == short.class || clazzField == Short.class) {
				paramVal = rs.getShort(propertyName);
			} else if (clazzField == int.class || clazzField == Integer.class) {
				paramVal = rs.getInt(propertyName);
			} else if (clazzField == long.class || clazzField == Long.class) {
				paramVal = rs.getLong(propertyName);
			} else if (clazzField == float.class || clazzField == Float.class) {
				paramVal = rs.getFloat(propertyName);
			} else if (clazzField == double.class || clazzField == Double.class) {
				paramVal = rs.getDouble(propertyName);
			} else if (clazzField == boolean.class || clazzField == Boolean.class) {
				paramVal = rs.getBoolean(propertyName);
			} else if (clazzField == byte.class || clazzField == Byte.class) {
				paramVal = rs.getByte(propertyName);
			} else if (clazzField == char.class || clazzField == Character.class) {
				paramVal = rs.getCharacterStream(propertyName);
			} else if (clazzField == Date.class) {
				paramVal = rs.getTimestamp(propertyName);
			} else if (clazzField.isArray()) {
				paramVal = rs.getString(propertyName).split(",");	//以逗号分隔的字符串
			} 
			
			/**
			 * 
			 */
			if(paramVal == null){
			    continue ;
			}
			
			PropertyDescriptor pd = new PropertyDescriptor(propertyName,t.getClass());
			pd.getWriteMethod().invoke(t, paramVal);
		}
	}
	
	
}
